/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Piasy
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.github.piasy.rxscreenshotdetector;

import ohos.aafwk.ability.DataAbilityHelper;
import ohos.aafwk.ability.IDataAbilityObserver;
import ohos.app.Context;
import ohos.data.resultset.ResultSet;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.photokit.metadata.AVStorage;
import ohos.security.SystemPermission;

import io.reactivex.rxjava3.core.Observable;

import java.util.Locale;

import static ohos.bundle.IBundleManager.PERMISSION_GRANTED;

/**
 * Created by Piasy{github.com/Piasy} on 16/1/29.
 */
public class RxScreenshotDetector {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD000F00, RxScreenshotDetector.class.getSimpleName());
    private static final String[] PROJECTION = new String[] {
            AVStorage.Images.Media.DISPLAY_NAME, AVStorage.Images.Media.DATA,
            AVStorage.Images.Media.DATE_ADDED
    };
    private static final long DEFAULT_DETECT_WINDOW_SECONDS = 10;

    private final Context mContext;

    private String lastScreenshotPath = "";

    private RxScreenshotDetector(final Context context) {
        mContext = context;
    }

    /**
     * start screenshot detect, if permission not granted, the observable will terminated with
     * an onError event.
     *
     * <p>
     * <em>Warning:</em> The created observable keeps a strong reference to {@code context}.
     * Unsubscribe to free this reference.
     * <p>
     *
     * @param context calling context
     *
     * @return {@link Observable} that emits screenshot file path.
     */
    public static Observable<String> start(final Context context) {
        return new RxScreenshotDetector(context)
                .start();
    }

    private static boolean matchPath(String path) {
        return path.toLowerCase().contains("screenshot") || path.contains("截屏") ||
                path.contains("截图");
    }

    private static boolean matchTime(long currentTime, long dateAdded) {
        return Math.abs(currentTime - dateAdded) <= DEFAULT_DETECT_WINDOW_SECONDS;
    }

    /**
     * Method to check if the permission for storage reading is granted or not
     * @param context calling context
     * @return true if granted else false
     */
    public static boolean checkPermission(Context context) {
        return context.verifySelfPermission(SystemPermission.READ_USER_STORAGE) == PERMISSION_GRANTED
                || !context.canRequestPermission(SystemPermission.READ_USER_STORAGE);
    }

    private Observable<String> start() {
        if (mContext.verifySelfPermission(SystemPermission.READ_USER_STORAGE) == PERMISSION_GRANTED
                || !mContext.canRequestPermission(SystemPermission.READ_USER_STORAGE)) {
            return startAfterPermissionGranted(mContext);
        } else {
            return Observable.error(new SecurityException("Permission not granted"));
        }
    }

    private Observable<String> startAfterPermissionGranted(final Context context) {
        final DataAbilityHelper dataAbilityHelper = DataAbilityHelper.creator(context);

        return Observable.create(emitter -> {
            final IDataAbilityObserver dataAbilityObserver = () -> {
                ResultSet resultSet = null;
                try {
                    resultSet = dataAbilityHelper.query(
                            AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI,
                            PROJECTION, null);
                    if (resultSet != null && resultSet.goToLastRow()) {
                        String path = resultSet.getString(
                                resultSet.getColumnIndexForName(AVStorage.Images.Media.DATA));
                        long dateAdded = resultSet.getLong(resultSet.getColumnIndexForName(
                                AVStorage.Images.Media.DATE_ADDED));
                        long currentTime = System.currentTimeMillis() / 1000;
                        HiLog.info(LABEL_LOG, String.format(Locale.ROOT, "%s", "path: " + path + ", dateAdded: " + dateAdded +
                                ", currentTime: " + currentTime));
                        if (matchPath(path) && matchTime(currentTime, dateAdded)
                                && !path.equalsIgnoreCase(lastScreenshotPath)) {
                            lastScreenshotPath = path;
                            emitter.onNext(path);
                        }
                    }
                } catch (Exception e) {
                    HiLog.info(LABEL_LOG, String.format(Locale.ROOT, "%s", "open result set fail: " + e.getMessage()));
                } finally {
                    if (resultSet != null) {
                        resultSet.close();
                    }
                }
            };
            dataAbilityHelper.registerObserver(
                    AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, dataAbilityObserver);

            emitter.setCancellable(() -> {
                lastScreenshotPath = "";
                dataAbilityHelper.unregisterObserver(
                        AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI,
                        dataAbilityObserver);
            });
        });
    }
}
